﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Shapes;

namespace wpfTool
{
    public class PipelineView : IComparable
    {
        static int numOfInstacesCreated = 0;
        int id;

        Line theLine = null;
        Canvas theCanvas = null;
        internal ShaderWidgetConnectionInfo srcInfo = null;
        internal ShaderWidgetConnectionInfo dstInfo = null;
        internal Window1 MainWindow;

        SortedDictionary<ShaderBase, ShaderWidget> shaderWidgetLookUpTable = null;
        internal LinkedList<AttachLine> attachlineList;

        public SortedDictionary<ShaderBase, ShaderWidget> ShaderWidgetLookUpTable
        {
            get { return shaderWidgetLookUpTable; }
        }

        public Line AssistanceLine
        {
            get 
            {
                if (theLine == null)
                {
                    theLine = new Line();
                    theLine.Stroke = System.Windows.Media.Brushes.Black;
                    theLine.Visibility = Visibility.Visible;
                    canvas.Children.Add(theLine);
                }
                return theLine; 
            }
        }

        public Canvas canvas
        {
            get { return theCanvas; }
        }

        public PipelineView(Canvas cvs, Window1 wnd)
        {
            id = numOfInstacesCreated++;
            theCanvas = cvs;
            MainWindow = wnd;
            shaderWidgetLookUpTable = new SortedDictionary<ShaderBase, ShaderWidget>();
            attachlineList = new LinkedList<AttachLine>();

            // line event
            canvas.MouseLeftButtonUp += new System.Windows.Input.MouseButtonEventHandler(OnMouseButtonLeftUp);
            canvas.MouseMove += new System.Windows.Input.MouseEventHandler(OnMouseButtonMove);
        }

        void OnMouseButtonLeftUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            if (MainWindow.IsEditMode)
            {
                if (AssistanceLine == null)
                    return;
                canvas.Children.Remove(AssistanceLine);
                theLine = null;

                // reset ShaderConnectionInfo
                if (srcInfo != null && dstInfo != null)
                {
                    if (srcInfo.shaderWidget.SrcPortList[srcInfo.portIndex].Fill ==
                        dstInfo.shaderWidget.DstPortList[dstInfo.portIndex].Fill)
                    {
                        AddShaderConnectionEvent(this, srcInfo.shaderWidget.Shader, srcInfo.portIndex, dstInfo.shaderWidget.Shader, dstInfo.portIndex);
                        AttachLine al = new AttachLine(srcInfo.shaderWidget, srcInfo.portIndex,
                            dstInfo.shaderWidget, dstInfo.portIndex, this);
                        attachlineList.AddLast(al);
                    }
                }

                srcInfo = null;
                dstInfo = null;
            }
            else if (MainWindow.IsAddWidgetMode)
            {
                if(MainWindow.ReadyToAddShaderName != "N/A")
                    MainWindow.pipelineViewMgr.addShader(MainWindow.ReadyToAddShaderName, (int)e.GetPosition(canvas).X, (int)e.GetPosition(canvas).Y);
            }
        }

        /// <summary>
        /// The second part of the assistance line drawing logic, 
        /// should bind to PipelineView
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        internal void OnMouseButtonMove(object sender, System.Windows.Input.MouseEventArgs e)
        {
            if (MainWindow.IsEditMode)
            {
                if (e.LeftButton != System.Windows.Input.MouseButtonState.Pressed)
                    return;
                Point pos = new Point();

                pos = e.GetPosition(canvas);
                AssistanceLine.X2 = pos.X - 1;
                AssistanceLine.Y2 = pos.Y - 1;
            }
        }

        /// <summary>
        /// The first part of the assistance line drawing logic, 
        /// should bind to PipelineView
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        internal void OnMouseButtonLeftDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            if (MainWindow.IsEditMode)
            {
                Point pos = new Point();
                pos = e.GetPosition(canvas);

                AssistanceLine.X1 = pos.X;
                AssistanceLine.Y1 = pos.Y;
            }
        }

        internal void removeShaderConnection(ShaderBase srcShder, int srcPortIndex, ShaderBase dstShader, int dstPortIndex)
        {
            RemoveShaderConnectionEvent(this, srcShder, srcPortIndex, dstShader, dstPortIndex);
        }

        public event Window1.PipelineStructureChangeHandler AddShaderConnectionEvent;
        public event Window1.PipelineStructureChangeHandler RemoveShaderConnectionEvent;

        int IComparable.CompareTo(object obj)
        {
            return (obj as PipelineView).id.CompareTo(id);
        }
    }


    internal class ShaderWidgetConnectionInfo
    {
        public ShaderWidget shaderWidget;
        public int portIndex;

        public ShaderWidgetConnectionInfo(ShaderWidget wgt, int index)
        {
            shaderWidget = wgt;
            portIndex = index;
        }
    }

    internal class AttachLine
    {
        ShaderWidgetConnectionInfo srcWgt;
        ShaderWidgetConnectionInfo dstWgt;

        PipelineView pipeline;

        Polyline attachLine;

        public AttachLine(ShaderWidget wgtS, int indexS, ShaderWidget wgtD, int indexD, PipelineView plv)
        {
            srcWgt = new ShaderWidgetConnectionInfo(wgtS, indexS);
            dstWgt = new ShaderWidgetConnectionInfo(wgtD, indexD);

            pipeline = plv;

            createAttachLine();
            registerAttachLine();
        }

        void createAttachLine()
        {
            attachLine = new Polyline();
            Point[] pArray = new Point[4];
            attachLine.Points = new System.Windows.Media.PointCollection(pArray);

            attachLine.Stroke = srcWgt.shaderWidget.SrcPortList[srcWgt.portIndex].Fill;
            attachLine.Visibility = Visibility.Visible;

            // compute the coords of line points
            computeCoord();
        }

        void registerAttachLine()
        {
            pipeline.canvas.Children.Add(attachLine);

            // register ShaderWidget event
            srcWgt.shaderWidget.ShaderWidgetMove += new Window1.ShaderWidgetMoveHandler(OnShaderWidgetMove);
            ++srcWgt.shaderWidget.numOfRegistedAttachLines;

            dstWgt.shaderWidget.ShaderWidgetMove += new Window1.ShaderWidgetMoveHandler(OnShaderWidgetMove);
            ++dstWgt.shaderWidget.numOfRegistedAttachLines;

        }

        void computeCoord()
        {
            double deltaX = dstWgt.shaderWidget.X - srcWgt.shaderWidget.X - srcWgt.shaderWidget.Width;
            double deltaY = dstWgt.shaderWidget.Y - srcWgt.shaderWidget.Y;

            attachLine.Points[0] = new Point(srcWgt.shaderWidget.X + srcWgt.shaderWidget.Width, 
                srcWgt.shaderWidget.Y + srcWgt.shaderWidget.fileName.Height + srcWgt.shaderWidget.GridElementHeight / 2 
                + srcWgt.shaderWidget.GridHeight *  srcWgt.portIndex);

            attachLine.Points[3] = new Point(dstWgt.shaderWidget.X , dstWgt.shaderWidget.Y + dstWgt.shaderWidget.fileName.Height
                + dstWgt.shaderWidget.GridElementHeight / 2 + dstWgt.shaderWidget.GridHeight * dstWgt.portIndex);

            if(deltaX > deltaY)
            {
                attachLine.Points[1] = new Point(attachLine.Points[0].X + deltaX / 3, attachLine.Points[0].Y);
                attachLine.Points[2] = new Point(attachLine.Points[3].X - deltaX / 3, attachLine.Points[3].Y);
            }
            else
            {
                attachLine.Points[1] = new Point(attachLine.Points[0].X + deltaX / 2, attachLine.Points[0].Y + deltaY / 3);
                attachLine.Points[2] = new Point(attachLine.Points[3].X - deltaX / 2, attachLine.Points[3].Y - deltaY / 3);
            }

        }

        void unregisterAttachLine(object sender, RoutedEventArgs e)
        {
            pipeline.canvas.Children.Remove(attachLine);
            pipeline.removeShaderConnection(srcWgt.shaderWidget.Shader, srcWgt.portIndex,
                dstWgt.shaderWidget.Shader, dstWgt.portIndex);

            // unregister ShaderWidget event
            srcWgt.shaderWidget.ShaderWidgetMove -= OnShaderWidgetMove;
            dstWgt.shaderWidget.ShaderWidgetMove -= OnShaderWidgetMove;
        }

        internal void OnShaderWidgetMove(ShaderWidget wgt)
        {
            if (wgt == srcWgt.shaderWidget || wgt == dstWgt.shaderWidget)
                computeCoord();
        }
    }
}
